package com.lagou.server;

import com.lagou.classLoader.MyClassLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat启动类
 */
@SuppressWarnings("all")
public class Bootstrap {

    private int port = 8080;

    public static void main(String[] args) throws Exception {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start6();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void start1() throws IOException {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat";
            String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            socket.close();
        }
    }

    /**
     * 静态资源
     */
    private void start2() throws IOException {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：\n" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            //封装request对象
            Request request = new Request(socket.getInputStream());
            //封装response对象
            Response response = new Response(socket.getOutputStream());
            response.outputHtml(request.getUrl());
            socket.close();
        }
    }

    /**
     * 动态资源
     */
    private void start3() throws Exception {
        //加载解析类路径web.xml
        loadServlet1();
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：\n" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            //封装request对象
            Request request = new Request(socket.getInputStream());
            //封装response对象
            Response response = new Response(socket.getOutputStream());
            if (servletMap.containsKey(request.getUrl())) {//动态资源
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            } else {//静态资源
                response.outputHtml(request.getUrl());
            }
            socket.close();
        }
    }

    /**
     * 多线程（每个请求一个线程）
     */
    private void start4() throws Exception {
        //加载解析类路径web.xml
        loadServlet1();
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：\n" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProccessor requestProccessor = new RequestProccessor(socket, servletMap);
            requestProccessor.start();
        }
    }

    /**
     * 线程池
     */
    private void start5() throws Exception {
        /**
         * 加载解析类路径web.xml
         */
        loadServlet1();

        /**
         * corePoolSize：初始化/最低线程数
         * maximumPoolSize：最大线程数
         * keepAliveTime：空闲线程存活时间
         * unit：空闲存活时间单位
         * blockingQueue：阻塞队列
         * threadFactory：线程工厂
         * handler：拒绝策略
         */
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100l;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        /**
         * 创建线程池
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, blockingQueue, threadFactory, handler);

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：\n" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProccessor requestProccessor = new RequestProccessor(socket, servletMap);
            threadPoolExecutor.execute(requestProccessor);
        }
    }

    /**
     * servlet容器
     */
    private Map<String, HttpServlet> servletMap = new HashMap<>();

    /**
     * 加载解析类路径web.xml
     */
    private void loadServlet1() throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        List<Element> servletNodeList = rootElement.selectNodes("//servlet");
        for (Element servletElement : servletNodeList) {
            Element servletNameElement = (Element) servletElement.selectSingleNode("servlet-name");
            /**
             * myServlet
             */
            String servletName = servletNameElement.getStringValue();
            Element servletClassElement = (Element) servletElement.selectSingleNode("servlet-class");
            /**
             * com.lagou.servlet.MyServlet
             */
            String servletClass = servletClassElement.getStringValue();

            Element servletMappingElement = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            /**
             * /myServlet
             */
            String urlPattern = servletMappingElement.selectSingleNode("url-pattern").getStringValue();
            /**
             * Class.forName(servletClass)=this.getClass().getClassLoader().load(servletClass)
             */
            servletMap.put(urlPattern, (HttpServlet) this.getClass().getClassLoader().loadClass(servletClass).newInstance());
        }
    }

    /**
     * 多应用部署
     */
    private void start6() throws Exception {
        /**
         * 加载解析指定路径web.xml
         */
        loadServlet2();

        /**
         * 线程池配置
         */
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100l;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        /**
         * 创建线程池
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, blockingQueue, threadFactory, handler);

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat启动：\n" + port);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProccessor requestProccessor = new RequestProccessor(socket, servletMap);
            threadPoolExecutor.execute(requestProccessor);
        }
    }

    /**
     * webapps绝对路径
     */
    private static final String webappsPath = "E:/LaoGou/tomcat/webapps";

    /**
     * 加载解析指定路径web.xml
     */
    private void loadServlet2() throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException, FileNotFoundException {
        //加载所有web.xml
        Map<String, InputStream> contextMap = loadContext();
        if (contextMap == null || contextMap.isEmpty()) {
            return;
        }
        //解析所有web.xml
        for (Map.Entry<String, InputStream> context : contextMap.entrySet()) {
            //基准uri：webapps目录下的文件夹名称
            String baseUri = context.getKey();
            //web.xml的输入流
            InputStream inputStream = context.getValue();
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            List<Element> servletNodeList = rootElement.selectNodes("//servlet");
            for (Element servletElement : servletNodeList) {
                Element servletNameElement = (Element) servletElement.selectSingleNode("servlet-name");
                /**
                 * servletName：myServlet
                 */
                String servletName = servletNameElement.getStringValue();
                Element servletClassElement = (Element) servletElement.selectSingleNode("servlet-class");
                /**
                 * servletClass：MyServlet
                 */
                String servletClass = servletClassElement.getStringValue();

                Element servletMappingElement = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                /**
                 * uriPattern：/myServlet
                 */
                String uriPattern = servletMappingElement.selectSingleNode("url-pattern").getStringValue();
                /**
                 * 使用自定义类加载器加载指定类，破坏双亲委派机制
                 */
                MyClassLoader myClassLoader = new MyClassLoader(webappsPath + "/" + baseUri);
                servletMap.put("/" + baseUri + uriPattern, (HttpServlet) myClassLoader.findClass(servletClass).newInstance());
            }
        }
    }

    /**
     * 加载应用
     */
    private Map<String, InputStream> loadContext() throws FileNotFoundException {
        Map<String, InputStream> contetxtMap = new HashMap<>();
        //扫描webapps目录
        File scanPackage = new File(webappsPath);
        File[] contexts = scanPackage.listFiles();
        if (contexts.length == 0) {
            return null;
        }
        for (File context : contexts) {
            if (!context.isDirectory()) {
                continue;
            }
            File[] files = context.listFiles();
            if (files.length == 0) {
                continue;
            }
            for (File file : files) {
                //加载所有web.xml文件
                if (!"web.xml".equals(file.getName())) {
                    continue;
                }
                InputStream inputStream = new FileInputStream(file);
                contetxtMap.put(context.getName(), inputStream);
            }
        }
        return contetxtMap;
    }
}
